package semanticAbstraction.wsmxAdapter;

import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import helper.stringHelper.StringHelper;
import helper.GroundingInformation;

import org.omwg.ontology.Attribute;
import org.omwg.ontology.Concept;
import org.omwg.ontology.Instance;
import org.omwg.ontology.Ontology;
import org.omwg.ontology.Value;
import org.wsmo.common.IRI;
import org.wsmo.common.Identifier;
import org.wsmo.common.Namespace;
import org.wsmo.common.exception.InvalidModelException;
import org.wsmo.common.exception.SynchronisationException;
import org.wsmo.factory.Factory;
import org.wsmo.factory.WsmoFactory;
import org.wsmo.service.Goal;
import org.wsmo.service.WebService;
import org.wsmo.wsml.ParserException;

import semanticAbstraction.interfaces.SemanticHandler;
import semanticAbstraction.wsmxAdapter.wsmlHelper.WSMOHelper;

public class WSMX_SemanticHandler implements SemanticHandler {
	

	public String invokeSemantically(String semanticDescription, String semanticInput) throws Exception {
		

		int idx=semanticDescription.indexOf("// -------");
		if(idx!=-1){
			semanticDescription=semanticDescription.substring(0, idx)+"\n";
		}
		
		
		
		//Goal goal = WSMOHelper.parseGoal(semanticDescription);
		
		String mergedMsg;
		
		mergedMsg = WSMOHelper.mergeGoalWithInstances(semanticDescription, semanticInput);
		
		
		String resultMsg;
		System.out.println("****\n"+mergedMsg);
		resultMsg=WSMX_Entrypoints.achieveGoal(mergedMsg);
		
		//resultMsg="ontology _\"http://org.ipsuper.composition.tp/CustomerIdentification#CustomerIdentificationOutput\"       instance _\"http://org.ipsuper.composition.tp/CustomerIdentification#customerIdentificationResponse\" memberOf _\"http://org.ipsuper.composition.tp/tpOntology#CustomerIdentificationResponse\"       _\"http://org.ipsuper.composition.tp/tpOntology#customer\" hasValue _\"http://org.ipsuper.composition.tp/CustomerIdentification#customerInstance\"     instance _\"http://org.ipsuper.composition.tp/CustomerIdentification#customerInstance\" memberOf _\"http://org.ipsuper.composition.tp/tpOntology#TP_Customer\"           _\"http://org.ipsuper.composition.tp/tpOntology#hasName\" hasValue \"Laurie\"        _\"http://org.ipsuper.composition.tp/tpOntology#hasCustomerID\" hasValue 151215";
		
		
		
		
		return resultMsg;
	}
	

	public GroundingInformation discoverWebService(String semanticDescription) throws Exception {

		String webServiceString;
		webServiceString=WSMX_Entrypoints.discoverWS(semanticDescription);


		System.out.println("Hello\n"+webServiceString);

		WebService ws;
		ws=WSMOHelper.parseWebService(webServiceString);


		GroundingInformation grounding;
		grounding=WSMOHelper.getWSDLLocation(ws);



		return grounding;
	}
	
	
	public String map(String sourceGoal, String sourceInstance, String targetGoal, String targetInstance, String sourceInput) {
		Ontology sourceOntology;
		try {

			sourceOntology = WSMOHelper.parseOntology(sourceInput);
			
			WsmoFactory wsmoFactory = Factory.createWsmoFactory(null);
			
			System.out.println(sourceOntology.getDefaultNamespace());
			
			String namespace;
			if(sourceOntology.getDefaultNamespace()!=null){
				namespace=sourceOntology.getDefaultNamespace().getIRI().toString().replace(sourceGoal, targetGoal);
			}else{
				namespace="http://org.ipsuper.composition.tp/"+targetGoal;
			}
			
			IRI namespaceIRI = wsmoFactory.createIRI(namespace);
			Namespace namespaceObj = wsmoFactory.createNamespace("", namespaceIRI);
			
			IRI tpNamespaceIRI = wsmoFactory.createIRI("http://org.ipsuper.composition.tp/tpOntology");
			Namespace tpNamespaceObj = wsmoFactory.createNamespace("", tpNamespaceIRI);
			
			Ontology targetOntology=wsmoFactory.createOntology(wsmoFactory.createIRI(namespaceObj, targetGoal+"Input"));
			
			Instance reqInstance;
			reqInstance=wsmoFactory.createInstance(wsmoFactory.createIRI(namespaceObj,targetGoal.substring(0, 1).toLowerCase()+targetGoal.substring(1)+"Request"));
			reqInstance.addConcept(wsmoFactory.createConcept(wsmoFactory.createIRI(tpNamespaceObj, targetGoal+"Request")));
			targetOntology.addInstance(reqInstance);
			
			for(Instance instance:sourceOntology.listInstances()){
				
				for(Identifier id:instance.listAttributeValues().keySet()){
					if(id.toString().endsWith(sourceInstance)){

						for(Value val:instance.listAttributeValues(id)){
							
							if(val instanceof Instance){
								Instance theInstance;
								theInstance=(Instance)val;
								
								
								reqInstance.addAttributeValue(id, val);
								targetOntology.addInstance(theInstance);
								addNestedInstances(sourceOntology, targetOntology, theInstance);
							}
						}
					}
				}
				/*
				for(Concept concept:instance.listConcepts()){
					if(concept.getIdentifier().toString().indexOf(sourceInstance)!=-1){
						reqInstance.addAttributeValue(concept.getIdentifier(), instance);
						targetOntology.addInstance(instance);

					}
				
				}*/
			}
			
			String tmp;
			
			tmp=WSMOHelper.serializeOntology(targetOntology);
			
			tmp=tmp.replace("/"+sourceGoal+"#", "/"+targetGoal+"#");
			
			return tmp;
			
		} catch (ParserException e) {

			e.printStackTrace();
		} catch (InvalidModelException e) {

			e.printStackTrace();
		}
	
		return "";

	}
	
	/**
	 * If we want to add an instance into another ontology, we must also add the nested instances. This method accomplishes this topic.
	 * @param sourceOntology The source ontology.
	 * @param targetOntology The target ontology,
	 * @param instance The instance of the source ontology we want to add.
	 * @throws SynchronisationException
	 * @throws InvalidModelException
	 */
	private void addNestedInstances(Ontology sourceOntology, Ontology targetOntology, Instance instance) throws SynchronisationException, InvalidModelException{
		for(Identifier id:instance.listAttributeValues().keySet()){

			for(Value val:instance.listAttributeValues(id)){

				if(val instanceof Instance){
					Instance theInstance;
					theInstance=(Instance)val;

					//reqInstance.addAttributeValue(id, val);
					targetOntology.addInstance(theInstance);
					addNestedInstances(sourceOntology, targetOntology, theInstance);
				}
			}

		}
	}
	
	/*
	private void addNestedInstances2(Ontology sourceOntology, String sourceOntologyName, Ontology targetOntology, String targetOntologyName, Instance instance) throws SynchronisationException, InvalidModelException{
		for(Identifier id:instance.listAttributeValues().keySet()){

			for(Value val:instance.listAttributeValues(id)){

				if(val instanceof Instance){
					Instance theInstance;
					theInstance=(Instance)val;

					//reqInstance.addAttributeValue(id, val);
					targetOntology.addInstance(theInstance);
					addNestedInstances2(sourceOntology, sourceOntologyName, targetOntology, targetOntologyName, theInstance);
				}
			}

		}
	}*/


	public boolean decide(String semanticInput, String condition) {
		StringTokenizer strTok;
		strTok=new StringTokenizer(condition, "AND");
		
		boolean result;
		result=true;
		while(strTok.hasMoreTokens()){
			String tmp;
			tmp=strTok.nextToken();
			result=result && semanticInput.contains(tmp.trim());

		}
		
		
		
		return result;
	}
	

	public String merge(String input1, String ontologyName1, String instanceName1, String input2, String ontologyName2, String instanceName2, String outputOntologyName){
		WsmoFactory wsmoFactory = Factory.createWsmoFactory(null);
		
		try {
			Ontology ontology1 = WSMOHelper.parseOntology(input1);
			Ontology ontology2 = WSMOHelper.parseOntology(input2);
		
			
			//String namespace=ontology1.getDefaultNamespace().getIRI().toString().replace(ontologyName1, outputOntologyName);
			
			String namespace;
			if(ontology1.getDefaultNamespace()!=null){
				namespace=ontology1.getDefaultNamespace().getIRI().toString().replace(ontologyName1, outputOntologyName);
			}else{
				namespace="http://org.ipsuper.composition.tp/"+outputOntologyName;
			}	
				
			IRI namespaceIRI = wsmoFactory.createIRI(namespace);
			Namespace namespaceObj = wsmoFactory.createNamespace("", namespaceIRI);
			
			IRI tpNamespaceIRI = wsmoFactory.createIRI("http://org.ipsuper.composition.tp/tpOntology");
			Namespace tpNamespaceObj = wsmoFactory.createNamespace("", tpNamespaceIRI);
			
			
			Ontology outOntology=wsmoFactory.createOntology(wsmoFactory.createIRI(namespaceObj, outputOntologyName+"Input"));
			
			Instance reqInstance;
		
			reqInstance=wsmoFactory.createInstance(wsmoFactory.createIRI(namespaceObj,outputOntologyName.substring(0, 1).toLowerCase()+outputOntologyName.substring(1)+"Request"));
			reqInstance.addConcept(wsmoFactory.createConcept(wsmoFactory.createIRI(tpNamespaceObj, outputOntologyName+"Request")));
			
			
			outOntology.addInstance(reqInstance);
			
			mergeInstances(ontology1, ontologyName1, instanceName1, outOntology, reqInstance);
			
			mergeInstances(ontology2, ontologyName2, instanceName2, outOntology, reqInstance);

			String tmp;
			
			tmp=WSMOHelper.serializeOntology(outOntology);
			
			
			tmp=tmp.replace("/"+ontologyName1+"#", "/"+outputOntologyName+"#");
			tmp=tmp.replace("/"+ontologyName2+"#", "/"+outputOntologyName+"#");
			
			
			System.out.println(tmp);
			return tmp;
			
			
		
		} catch (ParserException e) {
			e.printStackTrace();
		} catch (InvalidModelException e) {
			e.printStackTrace();
		}
		
		return "";
	}
	
	/**
	 * Extracts the relevant instances out of the passed ontology
	 * @param sourceOntology The source ontology, where we want to take the instances from.
	 * @param sourceOntologyName The name of the source ontology.
	 * @param instanceName The name of the instance we want to extract from the ontology. If no name is specified, all instances are taken.
	 * @param targetOntology The target ontology, where we want to store the extracted instance(s).
	 * @param targetOntologyRequestInstance The main instance of the target ontology.
	 * @throws SynchronisationException
	 * @throws InvalidModelException
	 */
	private void mergeInstances(Ontology sourceOntology, String sourceOntologyName, String instanceName, Ontology targetOntology, Instance targetOntologyRequestInstance) throws SynchronisationException, InvalidModelException{
		Instance mainInstance;
		
		// get the main instance
		mainInstance=findMainInstance(sourceOntology, sourceOntologyName);
		
		// do we want to take only the instance with the specified instanceName
		// or do we want to take all instances?
		boolean winnowInstances=false;
		
		// if instanceName is empty take all instances
		if(instanceName!=null){
			if(!instanceName.trim().equals("")){
				winnowInstances=true;
			}
		}
		
		Map<Identifier, Set<Value> > attrMap;
		attrMap=mainInstance.listAttributeValues();
		
		boolean takeInstance;
		
		// iterate over attributes 
		for(Identifier attrId:attrMap.keySet()){
			if(attrId instanceof IRI){
				IRI attrIri = (IRI)attrId;
				
				if(winnowInstances){
					if(attrIri.getLocalName().equals(instanceName.trim())){
						takeInstance=true;
					}else{
						takeInstance=false;
					}
				}else{
					takeInstance=true;
				}
				
				if(takeInstance){
					for(Value val:mainInstance.listAttributeValues(attrIri)){
						System.out.println("  "+val.toString());
						if(val instanceof Instance){
							Instance theInstance;
							theInstance=(Instance)val;


							targetOntologyRequestInstance.addAttributeValue(attrId, val);
							targetOntology.addInstance(theInstance);
							addNestedInstances(sourceOntology, targetOntology, theInstance);
						}
					}
				}
				
			}
		}
	}
	
	/**
	 * Every considered instance ontology has a main instance, which either ends with "Request" or with "Response". 
	 * This method finds this main instance of the specified ontology. 
	 * @param ontology
	 * @param ontologyName
	 * @return
	 */
	private Instance findMainInstance(Ontology ontology, String ontologyName){
		for(Instance instance:ontology.listInstances()){
			Identifier id;
			id=instance.getIdentifier();
			if(id instanceof IRI){
				IRI iri = (IRI)id;
				String localName1=iri.getLocalName().toLowerCase();

				if(localName1.equals(ontologyName.toLowerCase()+"request") || localName1.equals(ontologyName.toLowerCase()+"response")){
					return instance;
				}
			}
		}
		return null;
	}
	
	
	public static void main(String[] args) throws IOException{
		//testMerge();
		testMap();
		//testDecision();
	}
	
	
	public static void testMerge() throws IOException{
		
		WSMX_SemanticHandler sh =new WSMX_SemanticHandler();
		
		String filePath1="D:/DA/Development/TP_Showcase/Output/04 OrderCreation.wsml";
		String filePath2="D:/DA/Development/TP_Showcase/Output/07 ContractPreparation.wsml";
		
		String wsmlDoc1;
		String wsmlDoc2;
		wsmlDoc1=StringHelper.readFile(filePath1);
		wsmlDoc2=StringHelper.readFile(filePath2);
			  
		
		
		sh.merge(wsmlDoc1, "OrderCreation", /*"customerCase"*/"", wsmlDoc2, "ContractPreparation", /*"contract"*/"", "CourierService");
	}
	
	
	public static void testMap() throws IOException{
		WSMX_SemanticHandler sh =new WSMX_SemanticHandler();
		
		//String filePath="D:/DA/Development/TP_Showcase/Output/05 OrderConfirmation.wsml";
		String filePath="c:/test.txt";
		
		String wsmlDoc;
		
		wsmlDoc=StringHelper.readFile(filePath);
		
		String tmp;
		
		tmp=sh.map("CustomerIdentification", "customer", "FormalVerification", "customer", wsmlDoc);
		
		System.out.println(tmp);
	}
	
	public static void testDecision() throws IOException{
		WSMX_SemanticHandler sh =new WSMX_SemanticHandler();
		
		String filePath="D:/DA/Development/TP_Showcase/Output/MergedVerification.wsml";
		
		String wsmlDoc;
		
		wsmlDoc=StringHelper.readFile(filePath);
		
		boolean test;
		test=sh.decide(wsmlDoc, "hasTechnicalVerificationResult\" hasValue 1 AND hasFormalVerificationResult\" hasValue 1");
		System.out.println(test);
	}
	
}